<?php 
// +----------------------------------------------------------------------+
// |                          Mailcertificado                             |
// +----------------------------------------------------------------------+
// |                     www.mailcertificado.com                          |
// +----------------------------------------------------------------------+
// |           Copyright (c) 2012 Servicios de Mailcertificado            |
// +----------------------------------------------------------------------+
// | Este programa es software libre. Puede distribuirlo  y/o modificarlo |
// | bajo los términos de la  licencia GNU General Public License  tal  y |
// | como la publica la Fundación de Software Libre, bajo la denominación |
// | GPL, en su versión 2 o posterior (versión inglesa).                  |
// |                                                                      |
// | This program is free software. You can redistribute it and/or modify |
// | it under the terms of the GNU General Public License as published by |
// | the Free Software Foundation; either version 2 or later of the GPL.  |
// +----------------------------------------------------------------------+
// | Autores/Authors:                                                     |
// |          Carlos Mora <carlos.mora@mailcertificado.com> 			  |
// +----------------------------------------------------------------------+
//
// $Id: Exp $

///////////////////////////// CONFIGURACIONES - CONFIGURATIONS  //////////////////////////////////
$config["functions_path"] = 'jpfunctions.php';
$config["rules_path"] = $_SERVER['DOCUMENT_ROOT'].'/libs/jpvalidator30/jprules.inc'; //Complete path to rules file
///////////////////////////// FIN DE LAS CONFIGURACIONES /////////////////////////////////////


class Jpvalidator{
	protected $lang;
	protected $data;
	protected $rules;
	protected $errors;
	private $config;
	
	function Jpvalidator($lang){
		global $config;
		$this->lang = $lang;
		$this->config['rules_path'] = $config["rules_path"];
		$this->config['functions_path'] = $config["functions_path"];
		$json_rules = file_get_contents($this->config['rules_path']);
		if(!$json_rules) return "I can't open ".$this->config['rules_path']." please check jpvalidator.php configuration";
		$this->rules = json_decode($json_rules);
		if(json_last_error()){
			return RULES_PATH." is not a correct json file.";
		}
		$this->copyDefaultErrMesages();
	}
	
	/**
	 * Valida un formulario POST o GET
	 * @param $_POST|$_GET $data
	 * @param string $formname Regla de formulario a utilizar
	 * @return mixed true|array Devuelve true o un array de errores si los hay
	 */
	function validateForm($data,$formname = ''){
		$this->data = $data;
		$this->errors = array();
		$fieldsArray = array(); //Array de campos y valores
		//Si hay campos obligatorios los añadimos al array
		if($formname != '' && isset($this->rules->forms->$formname)){
			//Para cada campo obligatorio
			foreach($this->rules->forms->$formname as $key => $fieldname){	
				$fieldsArray[$fieldname] = ''; //Creamos un campo vacio que será substituido por el real si viene o se quedrá en blanco si no
				$this->rules->fields->$fieldname->php->required->enabled = true; //Lo establecemos como requerido
			}
		}
		foreach($this->data as $key => $value){
			if(!is_array($value)){
				$fieldsArray[$key] = $value;
			}else{
				foreach($value as $subkey => $subvalue){
					$fieldsArray[$key.'["'.$subkey.'"]'] = $subvalue;
				}
			}
		}
		//Recorremos el array de campos y vamos comprobando uno por uno
		foreach($fieldsArray as $key => $value){
			$this->check($key,$value);
		}
		if(!empty($this->errors)) return $this->errors;
		return true;
	}
	
	/**
	 * Valida un único campo
	 * @param string $fieldname Nombre del campo a validar
	 * @param string $value Valor del elemento
	 * @return true|string Devuelve true o el mensaje de error si no se ha validado
	 */
	function validateField($field,$value){
		$this->errors = array();
		$this->check($field,$value);
		if(!empty($this->errors)) return reset($this->errors);
		return true;
	}
	
	/**
	 * Hace todas las comprobaciones sobre un determinado campo
	 * @param string $field Nombre del campo a validar
	 * @param string $value Valor del elemento
	 */
	private function check($field,$value){
		//Seleccionamos la regla a aplicar a este campo
		if(isset($this->rules->fields->$field)){ //Si existe una regla para el campo
			$rules = $this->rules->fields->$field;
		}else{ //En caso contrario se toma la regla por defecto
			$defaultRule = '--default';
			if(isset($this->rules->fields->$defaultRule)) $rules = $this->rules->fields->$defaultRule;
			else return true;
		}
		$error = $this->checkLenMaxMin($rules,$value);
		if($error !== true){ $this->errors[$field] = "$field: $error"; return false; }
		$error = $this->checkRules($rules,$value);
		if($error !== true){ $this->errors[$field] = "$field: $error"; return false; }
		$error = $this->checkFunction($rules,$value,$field);
		if($error !== true){ $this->errors[$field] = "$field: $error"; return false; }
		return true;
	}
	
	/**
	 * Comprueba la longitud máxima y mínima del campo, además de su valor máximo y mínimo si es numérico
	 * También comprueba si el campo esta vacío si es requerido
	 * @param Object $rule Objeto de reglas para el elemento
	 * @param string $value Valor del elemento
	 * @return boolean|string Devuelve true o un string con el error
	 */
	private function checkLenMaxMin($rule,$value){
		$lang = $this->lang;
		if($value == '' && $rule->php->required->enabled == 'true'){
			if(!isset($rule->php->required->msg->$lang)) return false;
			return $rule->php->required->msg->$lang;
		}
		if(empty($value)) return true; //Si está vacío y hemos llegado hasta aquí salimos
		if($rule->value->max != '' || $rule->value->min != ''){ //Si hay un valor mínimo o máximo se evalua como numérico
			$value = str_replace(',','.',$value);
			$isnum = preg_match('/^\d+(\.\d+)?$/i', $value);
			if($isnum) $value = floatval($value);
			if($value > floatval($rule->value->max) || $value < floatval($rule->value->min) || !$isnum){
				if(!isset($rule->value->msg->$lang)) return false;
				$msg = $rule->value->msg->$lang;
				$msg = str_replace('$1',$rule->value->min,$msg);
				$msg = str_replace('$2',$rule->value->max,$msg);
				return $msg; //La salida será el mensaje de error
			}
		}else{
			if($rule->length->min == '' || $rule->length->max == '') return true; //Si esta vacío sale
			if(strlen($value) > intval($rule->length->max) || strlen($value) < intval($rule->length->min)){
				if(!isset($rule->length->msg->$lang)) return false;
				$msg = $rule->length->msg->$lang;
				$msg = str_replace('$1',$rule->length->min,$msg);
				$msg = str_replace('$2',$rule->length->max,$msg);
				return $msg; //La salida será el mensaje de error
			}
		}
		return true;
	}
	
	/**
	 * Comprueba las expresiones regulares asignadas para los elementos dados
	 * @param object $rules Objeto de reglas para el elemento
	 * @param string $value Valor del elemento
	 */
	private function checkRules($rules,$value){
		$fieldrules = $rules->rules; //Reglas para el elemento que estamos comprobando
		if(empty($fieldrules) || empty($value)) return true; //Si está vacío no hacemos nada
		//Recorremos el array de reglas y comprobamos una por una
		foreach($fieldrules as $key => $valuereg){ //Para cada regla del elemento
			if(!isset($this->rules->rules->$valuereg)) continue; //Buscamos que exista la regla en el array de reglas, sino continuamos
			$regla = $this->rules->rules->$valuereg;
			$regexp = '/'.$regla->data.'/';
			$lang = $this->lang;
			if(!preg_match($regexp,$value)) return $regla->msg->$lang; //Si no coincide retornamos el error
		}
		return true; //Si llegamos aquí es que todas las reglas eran correctas
	}
	
	/**
	 * Ejecuta las funciones php para los campos que las tengan designadas
	 * @param Object $rules Objeto de reglas para el elemento
	 * @param string $value Valor del elemento
	 * @param string $field Nombre del campo
	 */
	private function checkFunction($rules,$value,$field){
			$function = $rules->php->pfunction; //Reglas para el elemento que estamos comprobando
			if($function == '') return true; //Si está vacío no hacemos nada
			include $this->config['functions_path'];
			if(!function_exists($function)) return "I can't find ".$function."() function";
			$result = $function($value,$this->lang,$field); //Ejecutamos la función
			if($result != 'true' && $result !== true) return $result;
			return true;
		}
		
	/**
	* Esta funcion copia los mensajes de default en todos los valores que no tengan un mensaje asignado
 	*/
	private function copyDefaultErrMesages(){
			$default = '--default';
			foreach($this->rules->fields as $field => $fieldrule){
				if($field == $default) continue; //Continue si es el campo default
				//Copiamos length.msg
				foreach($this->rules->fields->$default->length->msg as $lang => $message){
					if(!isset($this->rules->fields->$field->length->msg->$lang)) $this->rules->fields->$field->length->msg->$lang = $message;
				}
				//Copiamos value.msg
				foreach($this->rules->fields->$default->value->msg as $lang => $message){
					if(!isset($this->rules->fields->$field->value->msg->$lang)) $this->rules->fields->$field->value->msg->$lang = $message;
				}			
				//Copiamos html.required.msg
				foreach($this->rules->fields->$default->php->required->msg as $lang => $message){
					if(!isset($this->rules->fields->$field->php->required->msg->$lang)) $this->rules->fields->$field->php->required->msg->$lang = $message;
				}				
			}
	}
	
	
}
?>